home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume22 / nn6.4 / part17 < prev    next >
Encoding:
Internet Message Format  |  1990-06-07  |  54.4 KB

  1. Subject:  v22i051:  NN Newsreader, release 6.4, Part17/21
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 13586485 3298704d 4ddc70cc fbfacd67
  5.  
  6. Submitted-by: "Kim F. Storm" <storm@texas.dk>
  7. Posting-number: Volume 22, Issue 51
  8. Archive-name: nn6.4/part17
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # Contents:  account.c articles.c config.h-dist decode.c digest.c
  17. # Wrapped by storm@texas.dk on Sun May  6 18:20:09 1990
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. echo If this archive is complete, you will see the following message:
  20. echo '          "shar: End of archive 17 (of 22)."'
  21. if test -f 'account.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'account.c'\"
  23. else
  24.   echo shar: Extracting \"'account.c'\" \(10252 characters\)
  25.   sed "s/^X//" >'account.c' <<'END_OF_FILE'
  26. X/*
  27. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  28. X *
  29. X *    Accounting for news reading.
  30. X *
  31. X *    The nnacct program is called by nn it three cases:
  32. X *
  33. X *    - on startup (-q) to check for permission to run nn (at this time)
  34. X *    - when the :cost command is executed (-cUSAGE -r) to
  35. X *      produce a "cost sofar" report, and
  36. X *    - at exit (-uUSAGE -r) to add the USAGE to the user's account
  37. X *      and print a "cost" report.
  38. X *
  39. X *    It can also be invoked by nnusage to print a usage and cost
  40. X *    report for the current user (default), or by the super user
  41. X *    to produce a usage and cost report for all users.
  42. X *
  43. X *    Accumulated accounting information is saved in the file $DB/acct.
  44. X *
  45. X *    If ACCTLOG is defined, a sequential log is maintained in $DB/acctlog.
  46. X *
  47. X *    You can define a COST_PER_MINUTE and a COST_UNIT to make
  48. X *    the user get a cost calculation (maybe just for the fun of it -
  49. X *    you can't imagine how expensive it is to read news here :-).
  50. X *
  51. X *    The COST_PER_MINUTE should be the price per minute multiplied
  52. X *    by 100 (to allow prices like $0.03/minute).
  53. X *    The definitions below corresponds to 1 Kroner per minute.
  54. X */
  55. X
  56. X#define ACCTLOG            /* */
  57. X#define COST_PER_MINUTE    100    /* price(in UNITs)/min * 100 */
  58. X#define COST_UNIT        "Kr."    /* Currency */
  59. X
  60. X#include "config.h"
  61. X#include "options.h"
  62. X#include "proto.h"
  63. X
  64. Ximport char *db_directory;
  65. X
  66. X
  67. X/*
  68. X *     local authorization policy checking
  69. X *
  70. X *    return/exit values of policy_check (nnacct -P0) are:
  71. X *
  72. X *    0: access granted
  73. X *    1: access granted, but cannot post
  74. X *    2: access denied (not authorized)
  75. X *    3: access denied (not allowed at this time of day)
  76. X *    4: access denied (quota exceeded)
  77. X */
  78. X
  79. X#define DENY_ACCESS    0
  80. X#define FREE_ACCOUNT    1
  81. X#define ALL_HOURS    2
  82. X#define OFF_HOURS    3
  83. X
  84. X#define NO_POST        40    /* add if cannot post */
  85. X
  86. X/*
  87. X *    DEFAULT POLICY AND QUOTA FOR NEW USERS
  88. X *
  89. X *    Notice that QUOTA is measued in hours.
  90. X *    Both ACCOUNTING and AUTHORIZATION must be defined for
  91. X *    the quota mechanism to work.
  92. X */
  93. X
  94. X#define DEFAULT_POLICY     ALL_HOURS    /* all time w/accounting */
  95. X#define DEFAULT_QUOTA     0        /* unlimited use */
  96. X
  97. X#ifdef AUTHORIZE
  98. X
  99. X#include <time.h>
  100. X
  101. Xstatic holiday(tm)
  102. Xstruct tm *tm;
  103. X{
  104. X    if (tm->tm_mon == 3 && tm->tm_mday == 23) return 1;    /* my birthday */
  105. X    if (tm->tm_mon == 11 && tm->tm_mday == 25) return 1;    /* another birthday */
  106. X    /* ... */
  107. X    return 0;
  108. X}
  109. X
  110. Xstatic policy_check(policy)
  111. Xint policy;
  112. X{
  113. X    struct tm *tm, *localtime();
  114. X    time_t t;
  115. X    int no_post = 0;
  116. X
  117. X    if (policy >= NO_POST) {
  118. X    policy -= NO_POST;
  119. X    no_post = 1;
  120. X    }
  121. X    
  122. X    switch (policy % 10) {
  123. X     case DENY_ACCESS:
  124. X    return 2;
  125. X
  126. X     case ALL_HOURS:
  127. X     case FREE_ACCOUNT:
  128. X    break;
  129. X
  130. X     case OFF_HOURS:    /* adapt this to your local requirements */
  131. X    time(&t);
  132. X    tm = localtime(&t);
  133. X    if (tm->tm_wday == 0) break;    /* Sunday */
  134. X    if (tm->tm_wday == 6) break;    /* Saturday */
  135. X    if (tm->tm_hour < 9) break;    /* morning */
  136. X    if (tm->tm_hour > 16) break;    /* evening */
  137. X    if (holiday(tm)) break;        /* holidays */
  138. X    /* etc. */
  139. X    return 3;
  140. X
  141. X     default:
  142. X    return 2;
  143. X    }
  144. X
  145. X    return no_post;
  146. X}
  147. X
  148. X#endif
  149. X
  150. Xstatic int add_usage = -1;
  151. Xstatic int show_cost = -1;
  152. Xstatic int report = 0;
  153. Xstatic int report_all = 0;
  154. Xstatic int quiet = 0;
  155. Xstatic char *acct_file = NULL;
  156. Xstatic int ck_policy = -1;
  157. Xstatic int new_policy = -1;
  158. Xstatic int new_quota = -1;
  159. Xstatic int who_am_caller = I_AM_ACCT;
  160. X
  161. XOption_Description(acct_options) {
  162. X    'C', Int_Option(show_cost),
  163. X    'U', Int_Option(add_usage),
  164. X    'W', Int_Option(who_am_caller),
  165. X    'P', Int_Option(ck_policy),
  166. X    'a', Bool_Option(report_all),
  167. X    'f', String_Option(acct_file),
  168. X    'p', Int_Option(new_policy),
  169. X    'q', Int_Option(new_quota),
  170. X    'r', Bool_Option(report),
  171. X    'Q', Bool_Option(quiet),
  172. X    '\0',
  173. X};
  174. X
  175. X/*
  176. X *    Accounting information:
  177. X *
  178. X *    xxxxxxx 00000000 00000000 00 00000\n
  179. X *
  180. X *    login    time used last     pol quota
  181. X *    name    (minutes) active icy (hours)
  182. X *
  183. X *    See the printf/scanf formats later on.
  184. X */
  185. X
  186. X
  187. X#define INPUT_FMT    "%s %ld %lx %d %d\n"
  188. X#define OUTPUT_FMT    "%s %08ld %08lx %02d %05d\n"
  189. X
  190. Xstruct account {
  191. X    off_t ac_offset;    /* offset in acct file */
  192. X    int ac_found;    /* present in acct file */
  193. X    
  194. X    char ac_user[24];    /* user name */
  195. X    
  196. X    long ac_total;    /* total usage */
  197. X    time_t ac_last;    /* last active */
  198. X    int ac_policy;    /* assigned policy */
  199. X    int ac_quota;    /* time quota */
  200. X};
  201. X
  202. Xstatic get_entry(acctf, user, ac)
  203. XFILE *acctf;
  204. Xchar *user;
  205. Xstruct account *ac;
  206. X{
  207. X    char line[100];
  208. X
  209. X    if (acctf != NULL && user != NULL) 
  210. X    rewind(acctf);
  211. X    
  212. X    ac->ac_found = 0;
  213. X
  214. X    for (;;) {
  215. X    ac->ac_policy = DEFAULT_POLICY;
  216. X    ac->ac_last = 0;
  217. X    ac->ac_total = 0;
  218. X    ac->ac_quota = DEFAULT_QUOTA;
  219. X    ac->ac_user[0] = NUL;
  220. X    
  221. X    if (acctf == NULL) break;
  222. X    
  223. X    ac->ac_offset = ftell(acctf);
  224. X
  225. X    if (fgets(line, 100, acctf) == NULL) break;
  226. X
  227. X    sscanf(line, INPUT_FMT, 
  228. X           ac->ac_user, &ac->ac_total, &ac->ac_last, 
  229. X           &ac->ac_policy, &ac->ac_quota);
  230. X
  231. X    if (user == NULL) return 1;
  232. X
  233. X    if (strcmp(user, ac->ac_user) == 0) {
  234. X        ac->ac_found = 1;
  235. X        return 1;
  236. X    }
  237. X    }
  238. X
  239. X    if (user != NULL) strcpy(ac->ac_user, user);
  240. X    return 0;
  241. X}
  242. X
  243. Xput_entry(acctf, ac)
  244. XFILE *acctf;
  245. Xstruct account *ac;
  246. X{
  247. X    if (ac->ac_found)
  248. X    fseek(acctf, ac->ac_offset, 0);
  249. X    else
  250. X    fseek(acctf, (off_t)0, 2);
  251. X    
  252. X    fprintf(acctf, OUTPUT_FMT, 
  253. X        ac->ac_user, ac->ac_total, ac->ac_last,
  254. X        ac->ac_policy, ac->ac_quota);
  255. X}
  256. X
  257. Xstatic do_cost(ac, ses)
  258. Xstruct account *ac;
  259. Xint ses;
  260. X{
  261. X    long r;
  262. X    
  263. X#ifdef COST_PER_MINUTE
  264. X#ifndef COST_UNIT
  265. X#define COST_UNIT ""
  266. X#endif
  267. X    if (ses >= 0)
  268. X    printf("Cost this session: %ld %s   Period total:",
  269. X           ((long)ses * COST_PER_MINUTE) / 100, COST_UNIT);
  270. X    printf("%6ld %s",
  271. X       (ac->ac_total * COST_PER_MINUTE) / 100, COST_UNIT);
  272. X#endif
  273. X    if (ses >= 0 && ac->ac_quota > 0) {
  274. X    r = ac->ac_quota - ac->ac_total/60;
  275. X    printf("  Quota: %ld hour%s", r, plural(r));
  276. X    }
  277. X    fl;
  278. X}
  279. X
  280. Xstatic do_report(ac, hdr)
  281. Xstruct account *ac;
  282. Xint hdr;
  283. X{
  284. X    if (hdr) {
  285. X    printf("USER        USAGE  QUOTA  LAST_ACTIVE");
  286. X#ifdef COST_PER_MINUTE
  287. X    printf("   COST/PERIOD");
  288. X#endif
  289. X#ifdef AUTHORIZE
  290. X    printf("   POLICY");
  291. X#endif
  292. X    putchar(NL);
  293. X    }
  294. X
  295. X    printf("%-8.8s  %4ld.%02ld  %5d  %s  ",
  296. X       ac->ac_user,
  297. X       ac->ac_total/60, ac->ac_total%60,
  298. X       ac->ac_quota,
  299. X       ac->ac_last ? date_time(ac->ac_last) : "");
  300. X#ifdef COST_PER_MINUTE
  301. X    do_cost(ac, -1);
  302. X#endif
  303. X#ifdef AUTHORIZE
  304. X    printf("     %2d  ", ac->ac_policy);
  305. X#endif
  306. X    printf("\n");
  307. X}
  308. X
  309. Xstatic do_report_all(acctf)
  310. XFILE *acctf;
  311. X{
  312. X    struct account ac;
  313. X    int first = 1;
  314. X    
  315. X    while (get_entry(acctf, (char *)NULL, &ac)) {
  316. X    do_report(&ac, first);
  317. X    first = 0;
  318. X    }
  319. X}
  320. X
  321. X
  322. X
  323. Xmain(argc, argv)
  324. Xint argc;
  325. Xchar *argv[];
  326. X{
  327. X    char *caller, *getlogin();
  328. X    FILE *acctf;
  329. X    char *fname;
  330. X    int users, i;
  331. X    struct account ac, *actab;
  332. X    
  333. X    who_am_i = I_AM_ACCT;
  334. X
  335. X    init_global();
  336. X
  337. X    users = parse_options(argc, argv, (char *)NULL, acct_options, "");
  338. X
  339. X    if (user_id != 0) {
  340. X    if (report_all) {
  341. X        fprintf(stderr, "Only root can request complete reports\n");
  342. X        exit(9);
  343. X    }
  344. X    if (new_policy >= 0) {
  345. X        fprintf(stderr, "Only root can change user authorization\n");
  346. X        exit(9);
  347. X    }
  348. X    if (new_quota >= 0) {
  349. X        fprintf(stderr, "Only root can change user quotas\n");
  350. X        exit(9);
  351. X    }
  352. X    if (users > 0) {
  353. X        fprintf(stderr, "Only root can request reports for other users\n");
  354. X        exit(9);
  355. X    }
  356. X
  357. X    if ((caller = getlogin()) == NULL)
  358. X        if ((caller = user_name()) == NULL)
  359. X        caller = "UNKNOWN";
  360. X    } else
  361. X    caller = "root";
  362. X
  363. X    if ((new_policy >= 0 || new_quota >= 0) && users == 0) {
  364. X    fprintf(stderr, "usage: %s -pPOLICY -qQUOTA user...\n", argv[0]);
  365. X    exit(1);
  366. X    }
  367. X    
  368. X    if (add_usage == 0 && report) {
  369. X    show_cost = 0;
  370. X    add_usage = -1;
  371. X    }
  372. X
  373. X    if (add_usage > 0 || new_policy >= 0 || new_quota >= 0) {
  374. X    if (acct_file) {
  375. X        fprintf(stderr, "Can only update current acct file\n", acct_file);
  376. X        exit(2);
  377. X    }
  378. X
  379. X    proto_lock(I_AM_ACCT, PL_SET_QUICK);
  380. X    }
  381. X
  382. X    if (acct_file) {
  383. X    if ((acctf = open_file(acct_file, OPEN_READ)) == NULL)
  384. X        acctf = open_file(relative(db_directory, acct_file), OPEN_READ);
  385. X    if (acctf == NULL) {
  386. X        fprintf(stderr, "Accounting file %s not found\n", acct_file);
  387. X        if (add_usage > 0 || new_policy >= 0 || new_quota >= 0)
  388. X        proto_lock(I_AM_ACCT, PL_CLEAR);
  389. X        exit(1);
  390. X    }
  391. X    } else {
  392. X    fname = relative(db_directory, "acct");
  393. X        acctf = open_file(fname, OPEN_READ);
  394. X    }
  395. X
  396. X    if (report_all) {
  397. X    do_report_all(acctf);
  398. X    fclose(acctf);
  399. X    exit(0);
  400. X    }
  401. X    
  402. X    if (ck_policy >= 0) {
  403. X#ifdef AUTHORIZE
  404. X    get_entry(acctf, caller, &ac);
  405. X    exit(policy_check(ac.ac_policy));
  406. X#else
  407. X    exit(0);
  408. X#endif
  409. X    }
  410. X    
  411. X    if (show_cost >= 0) {
  412. X    get_entry(acctf, caller, &ac);
  413. X    if (ac.ac_policy == FREE_ACCOUNT) exit(0);
  414. X    ac.ac_total += show_cost;
  415. X    do_cost(&ac, show_cost);
  416. X    exit(0);
  417. X    }
  418. X    
  419. X    if (add_usage > 0) {
  420. X    get_entry(acctf, caller, &ac);
  421. X    if (ac.ac_policy == FREE_ACCOUNT) goto unlock;
  422. X    ac.ac_total += add_usage;
  423. X    time(&ac.ac_last);
  424. X    } else
  425. X    if (users > 0) {
  426. X    actab = newobj(struct account, users + 1);
  427. X    for (i = 1; i <= users; i++) {
  428. X        get_entry(acctf, argv[i], &actab[i]);
  429. X        if (new_policy >= 0 || new_quota >= 0) {
  430. X        if (new_policy >= 0)
  431. X            actab[i].ac_policy = new_policy;
  432. X        if (new_quota >= 0)
  433. X            actab[i].ac_quota = new_quota;
  434. X        } else
  435. X        do_report(&actab[i], i == 1);
  436. X    }
  437. X    } else
  438. X    if (report) {
  439. X    if (get_entry(acctf, caller, &ac))
  440. X        do_report(&ac, 1);
  441. X    exit(0);
  442. X    }
  443. X    
  444. X    if (acctf) fclose(acctf);
  445. X
  446. X    if (add_usage <= 0 && new_policy < 0 && new_quota < 0) exit(0);
  447. X    
  448. X    umask(0177);
  449. X    acctf = open_file(fname, OPEN_UPDATE | MUST_EXIST);
  450. X    
  451. X    if (new_policy >= 0 || new_quota >= 0) {
  452. X    for (i = 1; i <= users; i++)
  453. X        put_entry(acctf, &actab[i]);
  454. X    fclose(acctf);
  455. X    goto unlock;
  456. X    }
  457. X
  458. X    if (add_usage > 0) {
  459. X    put_entry(acctf, &ac);
  460. X    if (report) {
  461. X#ifdef COST_PER_MINUTE
  462. X        do_cost(&ac, add_usage);
  463. X#else
  464. X        strcpy(ac.ac_user, "Total:");
  465. X        do_report(&ac, 0);
  466. X#endif
  467. X    }
  468. X    fclose(acctf);
  469. X#ifdef ACCTLOG
  470. X    fname = relative(db_directory, "acctlog");
  471. X        acctf = open_file(fname, OPEN_APPEND | MUST_EXIST);
  472. X    fprintf(acctf, "%s\t%s\t%ld\n",
  473. X        caller, date_time(ac.ac_last), (long)add_usage);
  474. X    fclose(acctf);
  475. X#endif
  476. X    goto unlock;
  477. X    }
  478. X
  479. X unlock:
  480. X    proto_lock(I_AM_ACCT, PL_CLEAR);
  481. X    exit(0);
  482. X    /*NOTREACHED*/
  483. X}
  484. X
  485. Xnn_exit(n)
  486. X{
  487. X    exit(n);
  488. X}
  489. X
  490. Xuser_error()
  491. X{
  492. X    exit(2);
  493. X}
  494. X
  495. X#ifdef HAVE_JOBCONTROL
  496. Xsuspend_nn()
  497. X{}
  498. X#endif
  499. END_OF_FILE
  500.   if test 10252 -ne `wc -c <'account.c'`; then
  501.     echo shar: \"'account.c'\" unpacked with wrong size!
  502.   fi
  503.   # end of 'account.c'
  504. fi
  505. if test -f 'articles.c' -a "${1}" != "-c" ; then 
  506.   echo shar: Will not clobber existing file \"'articles.c'\"
  507. else
  508.   echo shar: Extracting \"'articles.c'\" \(10178 characters\)
  509.   sed "s/^X//" >'articles.c' <<'END_OF_FILE'
  510. X/*
  511. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  512. X *
  513. X *    Basic article access and management
  514. X */
  515. X
  516. X#include "config.h"
  517. X#include "db.h"
  518. X#include "articles.h"
  519. X#include "regexp.h"
  520. X
  521. Xexport int  seq_cross_filtering = 1;
  522. Xexport int  select_leave_next = 0;     /* ask to select left over art. */
  523. X
  524. X
  525. X/*
  526. X * memory management
  527. X */
  528. X
  529. Xstatic thunk
  530. X    dummy_str_t = {
  531. X    NULL,
  532. X    NULL,
  533. X    0L
  534. X    },
  535. X    dummy_art_t = {
  536. X    NULL,
  537. X    NULL,
  538. X    0L
  539. X    };
  540. X
  541. X
  542. Xstatic thunk *first_str_t = &dummy_str_t;
  543. Xstatic thunk *current_str_t = &dummy_str_t;
  544. Xstatic thunk *first_art_t = &dummy_art_t;
  545. Xstatic thunk *current_art_t = &dummy_art_t;
  546. Xstatic long  cur_str_size = 0, cur_art_size = 0;
  547. Xstatic char *next_str;
  548. Xstatic article_header *next_art;
  549. Xstatic article_header **art_array = NULL;
  550. X
  551. Xstatic article_number max_articles = 0, mem_offset = 0;
  552. X
  553. X/*
  554. X * allocate one article header
  555. X */
  556. X
  557. X#ifndef ART_THUNK_SIZE
  558. X#define ART_THUNK_SIZE    127
  559. X#endif
  560. X
  561. Xstatic new_thunk(t, ptr, size)
  562. Xthunk *t;
  563. Xchar *ptr;
  564. Xlong size;
  565. X{
  566. X    thunk *new;
  567. X
  568. X    new = newobj(thunk, 1);
  569. X
  570. X    new->next_thunk = t->next_thunk;
  571. X    t->next_thunk = new;
  572. X
  573. X    new->this_thunk = ptr;
  574. X    new->thunk_size = size;
  575. X}
  576. X
  577. X
  578. Xarticle_header *alloc_art()
  579. X{
  580. X    if (cur_art_size == 0) {
  581. X    if (current_art_t->next_thunk == NULL)
  582. X        new_thunk(current_art_t,
  583. X              (char *)newobj(article_header, ART_THUNK_SIZE),
  584. X              (long)ART_THUNK_SIZE);
  585. X
  586. X    current_art_t = current_art_t->next_thunk;
  587. X    next_art = (article_header *)current_art_t->this_thunk;
  588. X    cur_art_size = current_art_t->thunk_size;
  589. X    }
  590. X
  591. X    cur_art_size--;
  592. X    return next_art++;
  593. X}
  594. X
  595. X/*
  596. X * allocate a string of length 'len'
  597. X */
  598. X
  599. X#ifndef STR_THUNK_SIZE
  600. X#define STR_THUNK_SIZE    ((1<<14) - 32)    /* leave room for malloc header */
  601. X#endif
  602. X
  603. Xchar *alloc_str(len)
  604. Xint len;
  605. X{
  606. X    char *ret;
  607. X
  608. X    if (cur_str_size <= len) {    /* must be room for len+1 bytes */
  609. X    if (current_str_t->next_thunk == NULL)
  610. X        new_thunk(current_str_t,
  611. X              newstr(STR_THUNK_SIZE), (long)STR_THUNK_SIZE);
  612. X
  613. X    current_str_t = current_str_t->next_thunk;
  614. X    next_str = current_str_t->this_thunk;
  615. X    cur_str_size = current_str_t->thunk_size;
  616. X    }
  617. X
  618. X    ret = next_str;
  619. X    cur_str_size -= len + 1;
  620. X    next_str += len;
  621. X    *next_str++ = NUL;    /* string is null terminated */
  622. X
  623. X    return ret;
  624. X}
  625. X
  626. X/*
  627. X * "free" the allocated memory
  628. X */
  629. X
  630. Xfree_memory()
  631. X{
  632. X    current_str_t = first_str_t;
  633. X    current_art_t = first_art_t;
  634. X    cur_str_size  = 0;
  635. X    cur_art_size  = 0;
  636. X    n_articles      = 0;
  637. X}
  638. X
  639. X
  640. X/*
  641. X * mark/release memory
  642. X */
  643. X
  644. X
  645. Xmark_str(str_marker)
  646. Xstring_marker *str_marker;
  647. X{
  648. X    str_marker->sm_cur_t = current_str_t;
  649. X    str_marker->sm_size  = cur_str_size;
  650. X    str_marker->sm_next  = next_str;
  651. X}
  652. X
  653. Xrelease_str(str_marker)
  654. Xstring_marker *str_marker;
  655. X{
  656. X    current_str_t = str_marker->sm_cur_t;
  657. X    cur_str_size  = str_marker->sm_size;
  658. X    next_str      = str_marker->sm_next;
  659. X}
  660. X
  661. X
  662. Xmark_memory(mem_marker)
  663. Xmemory_marker *mem_marker;
  664. X{
  665. X    mark_str(&(mem_marker->mm_string));
  666. X
  667. X    mem_marker->mm_cur_t = current_art_t;
  668. X    mem_marker->mm_size  = cur_art_size;
  669. X    mem_marker->mm_next  = next_art;
  670. X
  671. X    mem_marker->mm_nart     = n_articles;
  672. X    mem_offset += n_articles;
  673. X
  674. X    n_articles = 0;
  675. X    articles = art_array + mem_offset;
  676. X}
  677. X
  678. Xrelease_memory(mem_marker)
  679. Xmemory_marker *mem_marker;
  680. X{
  681. X    release_str(&(mem_marker->mm_string));
  682. X
  683. X    current_art_t = mem_marker->mm_cur_t;
  684. X    cur_art_size  = mem_marker->mm_size;
  685. X    next_art      = mem_marker->mm_next;
  686. X
  687. X    n_articles = mem_marker->mm_nart;
  688. X
  689. X    mem_offset -= n_articles;
  690. X    articles = art_array + mem_offset;
  691. X}
  692. X
  693. X/*
  694. X * merge all memory chunks into one.
  695. X */
  696. X
  697. Xmerge_memory()
  698. X{
  699. X    n_articles += mem_offset;
  700. X    mem_offset = 0;
  701. X    articles = art_array;
  702. X}
  703. X
  704. X
  705. X/*
  706. X * save article header in 'articles' array
  707. X * 'articles' is enlarged if too small
  708. X */
  709. X
  710. X#define    FIRST_ART_ARRAY_SIZE    500    /* malloc header */
  711. X#define    NEXT_ART_ARRAY_SIZE    512
  712. X
  713. Xadd_article(art)
  714. Xarticle_header *art;
  715. X{
  716. X    if ((n_articles + mem_offset) == max_articles) {
  717. X    /* must increase size of 'articles' */
  718. X
  719. X    if (max_articles == 0) {
  720. X        /* allocate initial 'articles' array */
  721. X        max_articles = FIRST_ART_ARRAY_SIZE;
  722. X    } else {
  723. X        max_articles += NEXT_ART_ARRAY_SIZE;
  724. X    }
  725. X    art_array = resizeobj(art_array, article_header *, max_articles);
  726. X    articles = art_array + mem_offset;
  727. X    }
  728. X
  729. X    articles[n_articles] = art;
  730. X    n_articles++;
  731. X}
  732. X
  733. X
  734. Xaccess_group(gh, first_article, last_article, flags, mask)
  735. Xregister group_header *gh;
  736. Xarticle_number first_article, last_article;
  737. Xregister flag_type flags;
  738. Xchar *mask;
  739. X{
  740. X    group_header *cpgh;
  741. X    FILE *data;
  742. X    off_t data_offset;
  743. X    register article_header *ah;
  744. X    cross_post_number cross_post;
  745. X    int skip_digest, n;
  746. X    attr_type leave_attr, test_article();
  747. X    memory_marker mem_marker;
  748. X    static regexp *rexp = NULL;
  749. X    static char subptext[80];
  750. X
  751. X    if (first_article < gh->first_db_article)
  752. X    first_article = gh->first_db_article;
  753. X
  754. X    if (last_article > gh->last_db_article)
  755. X    last_article = gh->last_db_article;
  756. X
  757. X    if (last_article == 0 || first_article > last_article) return 0;
  758. X
  759. X    data = open_data_file(gh, 'd', OPEN_READ);
  760. X    if (data == NULL) return -10;
  761. X
  762. X    if ((data_offset = get_data_offset(gh, first_article)) == (off_t)(-1))
  763. X    return -11;
  764. X
  765. X
  766. X    if (mask == NULL) {
  767. X    if (rexp != NULL) {
  768. X        freeobj(rexp);
  769. X        rexp = NULL;
  770. X    }
  771. X    } else {
  772. X    if (*mask == '/') {
  773. X        mask++;
  774. X        if (rexp != NULL) {
  775. X        if (strncmp(mask, subptext, 80) != 0) {
  776. X            freeobj(rexp);
  777. X            rexp = NULL;
  778. X        }
  779. X        }
  780. X        if (rexp == NULL) {
  781. X        strncpy(subptext, mask, 80);
  782. X        rexp = regcomp(mask);
  783. X        if (rexp == NULL) return -1;
  784. X        }
  785. X        /* notice mask is still non-NULL */
  786. X    }
  787. X    }
  788. X
  789. X    if ((flags & (ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES)))
  790. X    leave_attr = 0;
  791. X    else if (select_leave_next)
  792. X    leave_attr = A_READ;    /* will prompt */
  793. X    else
  794. X    leave_attr = A_LEAVE_NEXT;
  795. X
  796. X    if (!(flags & ACC_SPEW_MODE))
  797. X    use_newsrc(gh, (flags & ACC_ORIG_NEWSRC) ? 1 : 0);
  798. X
  799. X    if ((flags & ACC_EXTRA_ARTICLES) == 0)
  800. X    mark_memory(&mem_marker);
  801. X
  802. X    ah = alloc_art();
  803. X
  804. X    skip_digest = 0;
  805. X
  806. X    fseek(data, data_offset, 0);
  807. X
  808. X    while (ftell(data) < gh->data_write_offset) {
  809. X    if (db_read_art(data) <= 0) {
  810. X        fclose(data);
  811. X        if ((flags & ACC_EXTRA_ARTICLES) == 0)
  812. X        release_memory(&mem_marker);
  813. X        return -2;
  814. X    }
  815. X
  816. X    if (db_hdr.dh_lpos == (off_t)0)
  817. X        continue;    /* article not accessible */
  818. X
  819. X    if (db_hdr.dh_number > gh->last_db_article
  820. X        || db_hdr.dh_number < gh->first_db_article)
  821. X        goto data_error;
  822. X
  823. X    if (skip_digest && db_data.dh_type == DH_SUB_DIGEST)
  824. X        continue;
  825. X
  826. X    skip_digest = 0;
  827. X
  828. X    if (db_hdr.dh_cross_postings && !(flags & ACC_ALSO_CROSS_POSTINGS)) {
  829. X        for (n = 0; n < db_hdr.dh_cross_postings; n++) {
  830. X        cross_post = NETW_CROSS_INT(db_data.dh_cross[n]);
  831. X        if (cross_post < 0 || cross_post >= master.number_of_groups)
  832. X            continue;
  833. X        cpgh = &active_groups[cross_post];
  834. X        if (cpgh == gh) {
  835. X            if (seq_cross_filtering) continue;
  836. X            n = db_hdr.dh_cross_postings;
  837. X            break;
  838. X        }
  839. X        if (cpgh->group_flag & G_UNSUBSCRIBED) continue;
  840. X
  841. X        if (!seq_cross_filtering) break;
  842. X        if (cpgh->preseq_index > 0 &&
  843. X            cpgh->preseq_index < gh->preseq_index) break;
  844. X        }
  845. X
  846. X        if (n < db_hdr.dh_cross_postings) {
  847. X        if (db_data.dh_type == DH_DIGEST_HEADER) skip_digest++;
  848. X        continue;
  849. X        }
  850. X    }
  851. X
  852. X    ah->flag = 0;
  853. X
  854. X    switch (db_data.dh_type) {
  855. X     case DH_DIGEST_HEADER:
  856. X        if (flags & ACC_DONT_SPLIT_DIGESTS)
  857. X        skip_digest++;
  858. X        else
  859. X        if ((flags & ACC_ALSO_FULL_DIGEST) == 0)
  860. X            continue;    /* don't want the full digest when split */
  861. X        ah->flag |= A_FULL_DIGEST;
  862. X        break;
  863. X     case DH_SUB_DIGEST:
  864. X        ah->flag |= A_DIGEST;
  865. X        break;
  866. X    }
  867. X
  868. X    ah->a_number = db_hdr.dh_number;
  869. X    if (ah->a_number > last_article) break;
  870. X
  871. X    if (flags & ACC_SPEW_MODE) {
  872. X        printf("%x:%s\n", (int)(gh->group_num), db_data.dh_subject);
  873. X        continue;
  874. X    }
  875. X
  876. X    ah->hpos = db_hdr.dh_hpos;
  877. X    ah->fpos = ah->hpos + (off_t)(db_hdr.dh_fpos);
  878. X    ah->lpos = db_hdr.dh_lpos;
  879. X
  880. X    ah->attr = test_article(ah);
  881. X
  882. X    if (ah->attr != A_READ && (flags & ACC_ONLY_READ_ARTICLES))
  883. X        continue;
  884. X
  885. X    if (rexp != NULL) {
  886. X        if (flags & ACC_ON_SUBJECT)
  887. X        if (regexec_cf(rexp, db_data.dh_subject)) goto match_ok;
  888. X        if (flags & ACC_ON_SENDER)
  889. X        if (regexec_cf(rexp, db_data.dh_sender)) goto match_ok;
  890. X        continue;
  891. X    } else
  892. X    if (mask != NULL) {
  893. X        if (flags & ACC_ON_SUBJECT)
  894. X        if (strmatch_cf(mask, db_data.dh_subject)) goto match_ok;
  895. X        if (flags & ACC_ON_SENDER)
  896. X        if (strmatch_cf(mask, db_data.dh_sender)) goto match_ok;
  897. X        continue;
  898. X    }
  899. X     match_ok:
  900. X
  901. X     attr_again:
  902. X
  903. X    switch (ah->attr) {
  904. X     case A_LEAVE:
  905. X        if (mask) {
  906. X        ah->attr = 0;
  907. X        break;
  908. X        }
  909. X
  910. X        if (leave_attr == A_READ) {
  911. X        clrdisp();
  912. X        prompt("Select left over articles in group %s? ", gh->group_name);
  913. X        leave_attr = yes(0) > 0 ? A_SELECT : A_LEAVE_NEXT;
  914. X        }
  915. X        ah->attr = leave_attr;
  916. X        goto attr_again;
  917. X
  918. X     case A_SELECT:
  919. X        if (mask) ah->attr = 0;
  920. X        break;
  921. X
  922. X     case A_READ:
  923. X        if (!(flags & (ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES)))
  924. X        if (ah->a_number > gh->last_article)
  925. X            continue;
  926. X
  927. X        /* FALL THRU */
  928. X
  929. X     case A_SEEN:
  930. X     case 0:
  931. X        if (flags & ACC_DO_KILL) {
  932. X        ah->sender = db_data.dh_sender;
  933. X        ah->subject = db_data.dh_subject;
  934. X        if (kill_article(ah)) continue;
  935. X        }
  936. X
  937. X        /* The 'P' command to a read group must show articles as read */
  938. X/******/    if (gh->unread_count <= 0) ah->attr = A_READ;
  939. X        break;
  940. X
  941. X     default:
  942. X        break;
  943. X    }
  944. X
  945. X    if (ah->name_length = db_hdr.dh_sender_length) {
  946. X        ah->sender = alloc_str((int)db_hdr.dh_sender_length);
  947. X        strcpy(ah->sender, db_data.dh_sender);
  948. X    } else
  949. X        ah->sender = "";
  950. X
  951. X    if (ah->subj_length = db_hdr.dh_subject_length) {
  952. X        ah->subject = alloc_str((int)db_hdr.dh_subject_length);
  953. X        strcpy(ah->subject, db_data.dh_subject);
  954. X    } else
  955. X        ah->subject = "";
  956. X
  957. X    ah->replies = db_hdr.dh_replies;
  958. X    ah->lines = db_hdr.dh_lines;
  959. X    ah->t_stamp = db_hdr.dh_date;
  960. X
  961. X    ah->a_group = (flags & ACC_MERGED_MENU) ? current_group : NULL;
  962. X
  963. X    add_article(ah);
  964. X    ah = alloc_art();
  965. X    }
  966. X
  967. X    fclose(data);
  968. X
  969. X    if ((flags & ACC_DONT_SORT_ARTICLES) == 0)
  970. X    sort_articles(-1);
  971. X
  972. X    return n_articles > 0 ? 1 : 0;
  973. X
  974. Xdata_error:
  975. X    log_entry('E', "%s: data inconsistency", gh->group_name);
  976. X    fclose(data);
  977. X    if ((flags & ACC_EXTRA_ARTICLES) == 0)
  978. X    release_memory(&mem_marker);
  979. X    return -12;
  980. X}
  981. X
  982. X
  983. END_OF_FILE
  984.   if test 10178 -ne `wc -c <'articles.c'`; then
  985.     echo shar: \"'articles.c'\" unpacked with wrong size!
  986.   fi
  987.   # end of 'articles.c'
  988. fi
  989. if test -f 'config.h-dist' -a "${1}" != "-c" ; then 
  990.   echo shar: Will not clobber existing file \"'config.h-dist'\"
  991. else
  992.   echo shar: Extracting \"'config.h-dist'\" \(11434 characters\)
  993.   sed "s/^X//" >'config.h-dist' <<'END_OF_FILE'
  994. X/**************************** NN CONFIGURATION ***************************
  995. X *
  996. X *    Configuration file for nn.
  997. X *
  998. X *    You must edit this file to reflect your local configuration
  999. X *    and environment.
  1000. X *
  1001. X *    Before editing this file, read the licence terms in the README
  1002. X *    file and the installation guidelines in the INSTALLATION file.
  1003. X *
  1004. X *    (c) Copyright 1990, Kim F. Storm.  All rights reserved.
  1005. X */
  1006. X
  1007. X#define    RELEASE     "6.4"
  1008. X
  1009. X#include <stdio.h>
  1010. X#include <ctype.h>
  1011. X
  1012. X
  1013. X/*********************** NETWORK DEPENDENT DEFINITIONS **********************
  1014. X *
  1015. X *    Define NETWORK_DATABASE if you share the database through NFS on
  1016. X *    a network with different, non-compatible machines, e.g. SUNs and
  1017. X *    VAXen, or SUN-3 and SUN-4, or if you are using different compilers
  1018. X *    on the same architecture.
  1019. X *
  1020. X *    In a homogenous network, you can leave it undefined for higher
  1021. X *    performance (no data conversion is needed).
  1022. X */
  1023. X
  1024. X/* #define NETWORK_DATABASE    /* */
  1025. X
  1026. X
  1027. X/********************************** NNTP *********************************
  1028. X *
  1029. X *     Define NNTP to enable nntp support.  If you are not using NNTP,
  1030. X *    just leave the following NNTP_* definitions as they are - they
  1031. X *    will be ignored anyway.
  1032. X *
  1033. X *    With NNTP, the nnmaster still maintains a local database of
  1034. X *    all article headers for fast access (and because NNTP does not
  1035. X *    support nn - yet), while the articles are fetched from the
  1036. X *    nntp server when they are read or saved.
  1037. X *
  1038. X *    You may still share this database through NFS locally (see the
  1039. X *    description of NETWORK_DATABASE above) if you don't want to
  1040. X *    have separate nn databases on all your local systems.
  1041. X *
  1042. X *    Consult the file NNTP for further information on the use of NNTP.
  1043. X */
  1044. X
  1045. X/* #define NNTP            /* */
  1046. X
  1047. X/*
  1048. X *    Define NNTP_SERVER to the name of a file containing the name of the
  1049. X *    nntp server.
  1050. X *
  1051. X *    It is vital that both the nnmaster and all nn users on a machine
  1052. X *    uses the same nntp server, because the nn database is synchronized
  1053. X *    with a specific news active file.
  1054. X *
  1055. X *    If the file name does not start with a slash, it is relative to
  1056. X *    LIB_DIRECTORY defined below.
  1057. X */
  1058. X
  1059. X#define NNTP_SERVER    "/usr/lib/nntp_server"
  1060. X
  1061. X/*
  1062. X *    Define NNTP_POST if you want nn to reject attempts to post via
  1063. X *    NNTP to a server, that disallows postings.
  1064. X *
  1065. X *    You should define this, if you use the NNTP based mini-inews for
  1066. X *    postings from NNTP clients.  If you use another mechanism, that
  1067. X *    does not involve NNTP, you should leave it undefined.
  1068. X */
  1069. X
  1070. X#define NNTP_POST             /* */
  1071. X
  1072. X/*
  1073. X *    NNTP's mini-inews seems to require that messages contain a complete
  1074. X *    header with Message-ID, Path, and Date fields which the normal inews
  1075. X *    generates itself.  If your mini-inews requires these headers to
  1076. X *    be present, define NNTP_MINI_INEWS_HEADER below.
  1077. X */
  1078. X
  1079. X#define NNTP_MINI_INEWS_HEADER    /* uses "broken" mini-inews */
  1080. X
  1081. X
  1082. X/***************** OPERATING SYSTEM DEPENDENT DEFINITIONS *******************
  1083. X *
  1084. X *      Include the appropriate s- file for your system below.
  1085. X *
  1086. X *    If a file does not exist for your system, you can use
  1087. X *    conf/s-template.h as a starting point for writing you own.
  1088. X */
  1089. X
  1090. X#include "s-sys5.h"
  1091. X
  1092. X/*
  1093. X *    Define DEFAULT_PAGER as the initial value of the 'pager' variable.
  1094. X *    nnadmin pipes shell command output though this command.
  1095. X */
  1096. X
  1097. X#define DEFAULT_PAGER        "pg -n -s"    /* system V */
  1098. X/* #define DEFAULT_PAGER    "more"            /* bsd */
  1099. X
  1100. X/*
  1101. X *    DEFAULT_PRINTER is the initial value of the 'printer' variable.
  1102. X *    nn's :print command pipes text into this command.
  1103. X */
  1104. X
  1105. X#define DEFAULT_PRINTER        "lp -s"        /* System V */
  1106. X/* #define DEFAULT_PRINTER    "lpr -p -JNEWS"    /* bsd */
  1107. X
  1108. X/*
  1109. X *     Define RESIZING to make nn understand dynamic window-resizing.
  1110. X *     (It uses the TIOCGWINSZ ioctl found on most 4.3BSD systems)
  1111. X */
  1112. X
  1113. X/* #define RESIZING        /* */
  1114. X
  1115. X
  1116. X/********************** MACHINE DEPENDENT DEFINITIONS **********************
  1117. X *
  1118. X *    Include the appropriate m- file for your system below.
  1119. X *
  1120. X *    If a file does not exist for your system, you can use
  1121. X *    conf/m-template.h as a starting point for writing you own.
  1122. X */
  1123. X
  1124. X#include "m-m680x0.h"
  1125. X
  1126. X
  1127. X/***************************** OWNERSHIP ***************************
  1128. X *
  1129. X *    Specify owner and group for installed files and programs.
  1130. X *
  1131. X *    The nnmaster will run suid/sgid to this owner and group.
  1132. X *
  1133. X *    The only requirements are that the ownership allows the
  1134. X *    nnmaster to READ the news related files and directories, and
  1135. X *    the ordinary users to read the database and execute the nn*
  1136. X *    programs.
  1137. X *
  1138. X *    Common choices are:  (news, news)  and   (your uid, your gid)
  1139. X */
  1140. X
  1141. X#define OWNER    "news"
  1142. X#define    GROUP    "news"
  1143. X
  1144. X
  1145. X/**************************** LOCALIZATION ****************************
  1146. X *
  1147. X *    Specify where programs and files are installed.
  1148. X *
  1149. X *    BIN_DIRECTORY    - the location of the user programs (mandatory)
  1150. X *
  1151. X *    LIB_DIRECTORY     - the location of auxiliary programs and files.
  1152. X *               (mandatory UNLESS ALL of the following are defined).
  1153. X *
  1154. X *    MASTER_DIRECTORY - the location of the master program (on server)
  1155. X *               (= LIB_DIRECTORY if undefined)
  1156. X *
  1157. X *    CLIENT_DIRECTORY - the location of auxiliary programs (on clients)
  1158. X *               (= LIB_DIRECTORY if undefined)
  1159. X *
  1160. X *    HELP_DIRECTORY   - the location of help files, online manual, etc.
  1161. X *               (= CLIENT_DIRECTORY/help if undefined)
  1162. X *
  1163. X *    CACHE_DIRECTORY     - if NNTP is used, nn uses this central directory
  1164. X *               to store working copies of articles on the local
  1165. X *               system.  If not defined, it stores the articles
  1166. X *               in each user's ~/.nn directory.
  1167. X *
  1168. X *    TMP_DIRECTORY    - temporary file storage.  Overriden by $TMPDIR.
  1169. X *               (= /usr/tmp if undefined).
  1170. X *
  1171. X *    LOG_FILE         - the location of nn's log file.
  1172. X *               (= LIB_DIRECTORY/Log if undefined).
  1173. X */
  1174. X
  1175. X#define BIN_DIRECTORY    "/usr/local/bin"
  1176. X#define LIB_DIRECTORY    "/usr/local/lib/nn"
  1177. X
  1178. X
  1179. X/**************************** DATABASE LOCATION **************************
  1180. X *
  1181. X *    Specify where the nn database should be installed.
  1182. X *
  1183. X *    If none of the following symbols are defined, the database will
  1184. X *    be contained in the NEWS_DIRECTORY in a separate .nn directory for
  1185. X *    master files and in files named .nnx and .nnd in each group's
  1186. X *    spool directory.  To use this scheme, the OWNER specified above
  1187. X *    must have write permission on the news spool directories.
  1188. X *
  1189. X *    You should also be careful not to specify a database directory
  1190. X *    if you access news via NNTP - you may not have the proper directories
  1191. X *    on your local system.  However, it is probably ok anyway if nnmaster
  1192. X *    runs on the nntp server.
  1193. X *
  1194. X *    To change the default behaviour, you can define the following
  1195. X *    symbols:
  1196. X *
  1197. X *    DB_DIRECTORY       - the directory containing the master files.
  1198. X *
  1199. X *    DB_DATA_DIRECTORY  - the directory containing the per-group files
  1200. X *                 (default is DB_DIRECTORY/DATA if undefined).
  1201. X *
  1202. X *    DB_LONG_NAMES       - use group's name rather than number when
  1203. X *                 building file names in DB_DATA_DIRECTORY.
  1204. X *         (The file system must support long file names!!)
  1205. X */
  1206. X
  1207. X#define DB_DIRECTORY    "/usr/spool/nn"
  1208. X
  1209. X
  1210. X/*************************** NEWS TRANSPORT **************************
  1211. X *
  1212. X *    Specify the location of your news programs and files
  1213. X *    You only need to specify these if you are not
  1214. X *    satisfied with the default settings.
  1215. X *
  1216. X *    NEWS_DIRECTORY         - The news spool directory.
  1217. X *                  Default: /usr/spool/news
  1218. X *
  1219. X *    NEWS_LIB_DIRECTORY    - The news lib directory.
  1220. X *                  Default: /usr/lib/news
  1221. X *
  1222. X *    INEWS_PATH        - The location of the inews program.
  1223. X *                  Default: NEWS_LIB_DIR/inews
  1224. X */
  1225. X
  1226. X#define NEWS_DIRECTORY        "/usr/spool/news"    /* */
  1227. X#define NEWS_LIB_DIRECTORY    "/usr/lib/news"        /* */
  1228. X
  1229. X/* #define INEWS_PATH        "/usr/lib/news/inews"    /* */
  1230. X
  1231. X
  1232. X/*************************** MAIL INTERFACE *************************
  1233. X *
  1234. X *    Specify a mailer that accepts a letter WITH a header IN THE TEXT.
  1235. X *
  1236. X *     A program named 'recmail' program is normally delivered with
  1237. X *    the Bnews system, or you can use sendmail -t if you have it.
  1238. X *
  1239. X *    The contrib/ directory contains two programs which you might
  1240. X *    be able to use with a little tweaking.
  1241. X */
  1242. X
  1243. X#define REC_MAIL    "/usr/lib/news/recmail"    /* non-sendmail */
  1244. X/* #define REC_MAIL    "/usr/lib/sendmail -t"    /* sendmail */
  1245. X
  1246. X
  1247. X/*
  1248. X *    Define HAVE_ROUTING if your mailer understands domain based
  1249. X *    adresses (...@...) and performs the necessary rerouting (e.g.
  1250. X *    Sendmail or Smail).
  1251. X *
  1252. X *    Otherwise, nn will provide a simple routing facility using
  1253. X *      routing information specified in the file LIB_DIRECTORY/routes.
  1254. X */
  1255. X
  1256. X#define HAVE_ROUTING            /* */
  1257. X
  1258. X/*
  1259. X *    If HAVE_ROUTING is NOT defined, nn needs to know the name of
  1260. X *     your host.  To obtain the host name it will use either of the
  1261. X *    'uname' or 'gethostname' system calls as specified in the s-
  1262. X *    file included above.
  1263. X *
  1264. X *    If neither 'uname' nor 'gethostname' is available, you must
  1265. X *    define HOSTNAME to be the name of your host.  Otherwise, leave
  1266. X *    it undefined (it will not be used anyway).
  1267. X */
  1268. X
  1269. X/* #define HOSTNAME    "myhost"    /* Not used if HAVE_ROUTING */
  1270. X
  1271. X/*
  1272. X *    Define APPEND_SIGNATURE if you want nn to ask users to append
  1273. X *    ~/.signature to mail messages (reply/forward/mail).
  1274. X *
  1275. X *    If the mailer defined in REC_MAIL automatically includes .signature
  1276. X *    you should not define this (it will fool people to include it twice).
  1277. X *
  1278. X *    I think 'recmail' includes .signature, but 'sendmail -t' doesn't.
  1279. X */
  1280. X
  1281. X/* #define APPEND_SIGNATURE        /* */
  1282. X
  1283. X/*
  1284. X *    BUG_REPORT_ADDRESS is the initial value of the bug-report-address
  1285. X *    variable which is used by the :bug command to report bugs in
  1286. X *    the nn software.
  1287. X */
  1288. X
  1289. X#define BUG_REPORT_ADDRESS    "nn-bugs@dkuug.dk"
  1290. X
  1291. X
  1292. X/*************************** DOCUMENTATION ***************************
  1293. X *
  1294. X *     Specify directories for the user and system manuals
  1295. X *
  1296. X *     Adapt this to your local standards; the manuals will be named
  1297. X *         $(MAN_DIR)/program.$(MAN_SECTION)
  1298. X *
  1299. X *    USER_MAN    - nn, nntidy, nngrep, etc.
  1300. X *    SYS_MAN        - nnadmin
  1301. X *    DAEMON_MAN    - nnmaster
  1302. X */
  1303. X
  1304. X#define USER_MAN_DIR     "/usr/man/man1"
  1305. X#define USER_MAN_SECTION     "1"
  1306. X
  1307. X#define SYS_MAN_DIR     "/usr/man/man1"
  1308. X#define SYS_MAN_SECTION     "1m"
  1309. X
  1310. X#define DAEMON_MAN_DIR     "/usr/man/man8"
  1311. X#define DAEMON_MAN_SECTION     "8"
  1312. X
  1313. X
  1314. X/************************** LOCAL POLICY *****************************
  1315. X *
  1316. X *    Define STATISTICS if you want to keep a record of how much
  1317. X *    time the users spend on news reading.
  1318. X *
  1319. X *    Sessions shorter than the specified number of minutes are not
  1320. X *    recorded (don't clutter up the log file).
  1321. X *
  1322. X *    Usage statistics is entered into the $LOG_FILE with code U
  1323. X */
  1324. X
  1325. X/* #define STATISTICS    5 /* minutes */
  1326. X
  1327. X/*
  1328. X *    Define ACCOUNTING if you want to keep accumulated accounting
  1329. X *    based on the statistics in a separate 'acct' file.  In this
  1330. X *    case, the accounting figures will be secret, and not be
  1331. X *    written to the Log file.  And the users will not be able to
  1332. X *    "decrease" their own account.
  1333. X *
  1334. X *    See account.c for optional cost calculation parameters.
  1335. X */
  1336. X
  1337. X/* #define ACCOUNTING        /* */
  1338. X
  1339. X/*
  1340. X *    Define AUTHORIZE if you want to restrict the use of nn to
  1341. X *    certain users or certain periods of the day.  Define both
  1342. X *    this and ACCOUNTING if you want to impose a usage quota
  1343. X *
  1344. X *    See account.c for implementing various access policies.
  1345. X */
  1346. X
  1347. X/* #define AUTHORIZE    /* */
  1348. X
  1349. X/*
  1350. X *    Default folder directory
  1351. X */
  1352. X
  1353. X#define FOLDER_DIRECTORY    "~/News"
  1354. X
  1355. X/*
  1356. X *    Max length of authors name (in "edited" format).
  1357. X *    Also size of "Name" field on the article menus.
  1358. X *    You may want to increase this if your terminals are wider than
  1359. X *    80 columns.
  1360. X */
  1361. X
  1362. X#define NAME_LENGTH         16
  1363. X
  1364. X
  1365. X/************************ CONFIGURATION COMPLETED ************************/
  1366. X
  1367. X#include "global.h"
  1368. END_OF_FILE
  1369.   if test 11434 -ne `wc -c <'config.h-dist'`; then
  1370.     echo shar: \"'config.h-dist'\" unpacked with wrong size!
  1371.   fi
  1372.   # end of 'config.h-dist'
  1373. fi
  1374. if test -f 'decode.c' -a "${1}" != "-c" ; then 
  1375.   echo shar: Will not clobber existing file \"'decode.c'\"
  1376. else
  1377.   echo shar: Extracting \"'decode.c'\" \(10831 characters\)
  1378.   sed "s/^X//" >'decode.c' <<'END_OF_FILE'
  1379. X/*
  1380. X * Decode one or more uuencoded article back to binary form.
  1381. X *
  1382. X * UNIX/NN VERSION
  1383. X *    This version cannot be used as a stand-alone uud!
  1384. X *     This version is made: 16 June 1989.
  1385. X *
  1386. X * From the Berkeley original, modified by MSD, RDR, JPHD & WLS.
  1387. X */
  1388. X
  1389. X#include "config.h"
  1390. X
  1391. X/* #define DEC_DEBUG    /* never define this */
  1392. X
  1393. Xexport char *decode_header_file = "Decode.Headers";
  1394. Xexport int decode_skip_prefix = 2;
  1395. X
  1396. X#define MAXCHAR 256
  1397. X#define LINELEN 256
  1398. X#define NORMLEN 60    /* allows for 80 encoded chars per line */
  1399. X
  1400. X#define SEQMAX 'z'
  1401. X#define SEQMIN 'a'
  1402. X
  1403. Xstatic char seqc, partn;
  1404. Xstatic int first, secnd, check;
  1405. X
  1406. X#define MAX_PREFIX 10
  1407. Xstatic int prefix_lgt, set_prefix;
  1408. Xstatic char prefix_str[MAX_PREFIX];
  1409. X
  1410. Xstatic FILE *out;
  1411. Xstatic char *target;
  1412. Xstatic char blank;
  1413. Xstatic int chtbl[MAXCHAR], cdlen[NORMLEN + 3];
  1414. Xstatic char ofname[FILENAME], arcname[FILENAME];
  1415. Xstatic int state, arcpart;
  1416. X
  1417. X#define    NO_ADVANCE        0x10
  1418. X
  1419. X#define    DECODE_TEXT            1
  1420. X#define    FIND_BEGIN            2
  1421. X#define    FIND_BEGIN_AFTER_ERROR        3
  1422. X#define FIND_BEGIN_AFTER_INCLUDE    4
  1423. X#define NEW_BEGIN            (5 | NO_ADVANCE)
  1424. X#define    FOUND_END                   (6 | NO_ADVANCE)
  1425. X#define FOUND_INCLUDE            (7 | NO_ADVANCE)
  1426. X#define    SKIP_LEADING            8
  1427. X#define    SKIP_TRAILING            (9 | NO_ADVANCE)
  1428. X#define DECODE_ERROR            (10 | NO_ADVANCE)
  1429. X#define OTHER_ERROR            (11 | NO_ADVANCE)
  1430. X
  1431. X#define MIN_DECODE_LEADING 8 /* lines to decode ok when doing skip-leading */
  1432. X
  1433. X#ifdef DEC_DEBUG
  1434. X    char *state_tbl[] = {
  1435. X    "-", "decode", "find begin", "find a/error", "find a/include",
  1436. X    "new begin", "found end", "found include", "skip leading",
  1437. X    "skip trail", "error", "other error"
  1438. X    };
  1439. X
  1440. X#endif
  1441. X
  1442. X/*
  1443. X * decode one line, write on out file
  1444. X */
  1445. X
  1446. Xstatic strncmp_skip(buf, str, n)
  1447. Xchar *buf, *str;
  1448. Xint n;
  1449. X{
  1450. X    register int i;
  1451. X    register char *line = buf;
  1452. X
  1453. X    if (!set_prefix)
  1454. X    return strncmp(line, str, n);
  1455. X
  1456. X    if (decode_skip_prefix > MAX_PREFIX) decode_skip_prefix = MAX_PREFIX;
  1457. X
  1458. X    for (i = 0; i <= decode_skip_prefix; i++, line++) {
  1459. X    if (*line == NUL) break;
  1460. X    if (*line == '#' || *line == ':') break;
  1461. X    if (strncmp(line, str, n)) continue;
  1462. X    prefix_lgt = i;
  1463. X    if (i) strncpy(prefix_str, buf, i);
  1464. X    set_prefix = 0;
  1465. X#ifdef DEC_DEBUG
  1466. X    msg("match %s", str);
  1467. X    user_delay(1);
  1468. X#endif
  1469. X    return 0;
  1470. X    }
  1471. X
  1472. X    return 1;
  1473. X}
  1474. X
  1475. Xstatic decode_line(buf, len, dont_write)
  1476. Xchar *buf;
  1477. Xregister int len;        /* actual input line length */
  1478. Xint dont_write;            /* doing leading check */
  1479. X{
  1480. X    char outl[LINELEN];
  1481. X    register char *bp, *ut;
  1482. X    register int *trtbl = chtbl;
  1483. X    register int n;
  1484. X    register int blen;        /* binary length (from decoded file) */
  1485. X    register int rlen;        /* calculated input line length */
  1486. X
  1487. X    /*
  1488. X     * Get the binary line length.
  1489. X     */
  1490. X    if ((blen = trtbl[buf[0]]) < 0) {
  1491. X    if (strncmp(buf, "begin", 5) == 0 || strncmp(buf, "table", 5) == 0)
  1492. X        return NEW_BEGIN;
  1493. X
  1494. X    if (state == SKIP_LEADING) return SKIP_LEADING;
  1495. X
  1496. X    /*
  1497. X     * end of uuencoded file ?
  1498. X     */
  1499. X    if (strncmp(buf, "end", 3) == 0)
  1500. X        return FOUND_END;
  1501. X
  1502. X    /*
  1503. X     * end of current file ? : get next one.
  1504. X     */
  1505. X    if (strncmp(buf, "include", 7) == 0)
  1506. X        return FOUND_INCLUDE;
  1507. X
  1508. X    /*
  1509. X     * trailing garbage
  1510. X     */
  1511. X    return SKIP_TRAILING;
  1512. X    }
  1513. X
  1514. X    rlen = cdlen[blen];
  1515. X    if (len < rlen) goto d_err;
  1516. X
  1517. X    /*
  1518. X     * Is it the empty line before the end line ?
  1519. X     */
  1520. X    if (blen == 0) return state;
  1521. X
  1522. X    /*
  1523. X     * Pad with blanks.
  1524. X     */
  1525. X    for (bp = buf + len, n = rlen - len; --n >= 0; ) *bp++ = blank;
  1526. X
  1527. X    /*
  1528. X     * Verify
  1529. X     */
  1530. X    for (n = rlen, bp = buf; --n >= 0; bp++)
  1531. X    if (trtbl[*bp] < 0) {
  1532. X#ifdef DEC_DEBUG
  1533. X        msg("%s - verify failed %d '%.30s'",
  1534. X        state_tbl[state&0xf], rlen - n, buf);
  1535. X        user_delay(2);
  1536. X#endif
  1537. X        goto d_err;
  1538. X    }
  1539. X
  1540. X    /*
  1541. X     * Check for uuencodes that append a 'z' to each line....
  1542. X     */
  1543. X    if (check)
  1544. X    if (secnd) {
  1545. X        secnd = 0;
  1546. X        if (buf[rlen] == SEQMAX) check = 0;
  1547. X    } else if (first) {
  1548. X        first = 0;
  1549. X        secnd = 1;
  1550. X        if (buf[rlen] != SEQMAX) check = 0;
  1551. X    }
  1552. X
  1553. X    /*
  1554. X     * There we check.
  1555. X     */
  1556. X    if (check) {
  1557. X    if (buf[rlen] != seqc) {
  1558. X#ifdef DEC_DEBUG
  1559. X        msg("check failed %d != %d", buf[rlen], seqc); user_delay(1);
  1560. X#endif
  1561. X        goto d_err;
  1562. X    }
  1563. X
  1564. X    if (--seqc < SEQMIN) seqc = SEQMAX;
  1565. X    }
  1566. X
  1567. X    if (dont_write) return DECODE_TEXT;
  1568. X
  1569. X    /*
  1570. X     * output a group of 3 bytes (4 input characters).
  1571. X     */
  1572. X    ut = outl;
  1573. X    n = blen;
  1574. X    bp = &buf[1];
  1575. X    while (--n >= 0) {
  1576. X    *(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4;
  1577. X    if (n > 0) {
  1578. X        *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2);
  1579. X        n--;
  1580. X    }
  1581. X    if (n > 0) {
  1582. X        *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]];
  1583. X        n--;
  1584. X    }
  1585. X    bp += 4;
  1586. X    }
  1587. X    if (fwrite(outl, 1, blen, out) <= 0) {
  1588. X    msg("Error on writing decoded file");
  1589. X    return OTHER_ERROR;
  1590. X    }
  1591. X
  1592. X    return DECODE_TEXT;
  1593. X
  1594. X d_err:
  1595. X    if (state == SKIP_LEADING) return SKIP_LEADING;
  1596. X    return DECODE_ERROR;
  1597. X
  1598. X}
  1599. X
  1600. X
  1601. X
  1602. X/*
  1603. X * Install the table in memory for later use.
  1604. X */
  1605. Xstatic inittbls()
  1606. X{
  1607. X    register int i, j;
  1608. X
  1609. X    /*
  1610. X     * Set up the default translation table.
  1611. X     */
  1612. X    for (i = 0; i < ' '; i++) chtbl[i] = -1;
  1613. X    for (i = ' ', j = 0; i < ' ' + 64; i++, j++) chtbl[i] = j;
  1614. X    for (i = ' ' + 64; i < MAXCHAR; i++) chtbl[i] = -1;
  1615. X    chtbl['`'] = chtbl[' '];    /* common mutation */
  1616. X    chtbl['~'] = chtbl['^'];    /* an other common mutation */
  1617. X    blank = ' ';
  1618. X    /*
  1619. X     * set up the line length table, to avoid computing lotsa * and / ...
  1620. X     */
  1621. X    cdlen[0] = 1;
  1622. X    for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4)
  1623. X    cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j));
  1624. X}
  1625. X
  1626. Xstatic gettable(in)
  1627. XFILE *in;
  1628. X{
  1629. X    char buf[LINELEN], *line;
  1630. X    register int c, n = 0;
  1631. X    register char *cpt;
  1632. X
  1633. X    for (c = 0; c <= MAXCHAR; c++) chtbl[c] = -1;
  1634. X
  1635. X    for (;;) {
  1636. X    if (fgets(buf, sizeof buf, in) == NULL) {
  1637. X        msg("EOF while in translation table.");
  1638. X        return -1;
  1639. X    }
  1640. X    line = buf + prefix_lgt;
  1641. X    if ((prefix_lgt > 0 && strncmp(buf, prefix_str, prefix_lgt)) ||
  1642. X        strncmp(line, "begin", 5) == 0) {
  1643. X        msg("Incomplete translation table.");
  1644. X        return -1;
  1645. X    }
  1646. X    cpt = line + strlen(line) - 1;
  1647. X    *cpt = ' ';
  1648. X    while (*(cpt) == ' ') {
  1649. X        *cpt = 0;
  1650. X        cpt--;
  1651. X    }
  1652. X    cpt = line;
  1653. X    while (c = *cpt) {
  1654. X        if (chtbl[c] != -1) {
  1655. X        msg("Duplicate char in translation table.");
  1656. X        return -1;
  1657. X        }
  1658. X        if (n == 0) blank = c;
  1659. X        chtbl[c] = n++;
  1660. X        if (n >= 64) return 0;
  1661. X        cpt++;
  1662. X    }
  1663. X    }
  1664. X}
  1665. X
  1666. Xstatic new_file()
  1667. X{
  1668. X    out = NULL;
  1669. X    seqc = SEQMAX;
  1670. X    partn = 'a';
  1671. X    check = 1;
  1672. X    first = 1;
  1673. X    secnd = 0;
  1674. X    state = FIND_BEGIN;
  1675. X    prefix_lgt = 0;
  1676. X    set_prefix = decode_skip_prefix;
  1677. X    arcpart = 0;
  1678. X    inittbls();
  1679. X}
  1680. X
  1681. Xuud_start(dir)
  1682. Xchar *dir;
  1683. X{
  1684. X    target = dir;
  1685. X    new_file();
  1686. X}
  1687. X
  1688. Xuud_end()
  1689. X{
  1690. X    if (out != NULL) {
  1691. X    fclose(out);
  1692. X    msg("%s INCOMPLETE -- removed", arcname);
  1693. X    unlink(ofname);
  1694. X    out = NULL;
  1695. X    }
  1696. X}
  1697. X
  1698. X
  1699. Xuudecode(ah, in)
  1700. Xregister article_header *ah;
  1701. XFILE *in;
  1702. X{
  1703. X    int mode, onedone, len, lead_check = 0;
  1704. X    int ostate = 0;
  1705. X    char buf[LINELEN], part[2], *line;
  1706. X    off_t real_size, start_offset;
  1707. X    long expect_size;
  1708. X
  1709. X    onedone = 0;
  1710. X
  1711. X    /*
  1712. X     * search for header or translation table line.
  1713. X     */
  1714. X    start_offset = ftell(in);
  1715. X
  1716. X    for (;;) {
  1717. X    if ((state & NO_ADVANCE) == 0) {
  1718. X        if (ftell(in) >= ah->lpos) break;
  1719. X        if (fgets(buf, sizeof buf, in) == NULL) break;
  1720. X    }
  1721. X
  1722. X    if (s_keyboard) return -1;
  1723. X
  1724. X    len=strlen(buf);
  1725. X    if (len > 0 && buf[len - 1] == NL) buf[--len] = NUL;
  1726. X
  1727. X#ifdef DEC_DEBUG
  1728. X    if (state != ostate) {
  1729. X        msg("%s->%s - '%.30s'", state_tbl[ostate&0xf], state_tbl[state&0xf], buf);
  1730. X        user_delay(2);
  1731. X        ostate = state;
  1732. X    }
  1733. X#endif
  1734. X    switch (state) {
  1735. X
  1736. X     case NEW_BEGIN:
  1737. X        if (out != NULL) {
  1738. X        uud_end();
  1739. X        user_delay(5);
  1740. X        }
  1741. X        new_file();
  1742. X        /* fall thru */
  1743. X
  1744. X     case FIND_BEGIN:
  1745. X     case FIND_BEGIN_AFTER_ERROR:
  1746. X     case FIND_BEGIN_AFTER_INCLUDE:
  1747. X        set_prefix = decode_skip_prefix;
  1748. X        if (strncmp_skip(buf, "table", 5) == 0) {
  1749. X        gettable(in);
  1750. X        continue;
  1751. X        }
  1752. X
  1753. X        if (strncmp_skip(buf, "begin", 5)) continue;
  1754. X
  1755. X        line = buf + prefix_lgt;
  1756. X
  1757. X        if (state == FIND_BEGIN_AFTER_INCLUDE) {
  1758. X        if(sscanf(line,"begin part %1s%s", part, arcname) != 2) {
  1759. X            msg("Invalid 'begin' line after 'include'");
  1760. X            continue;
  1761. X        }
  1762. X        partn++;
  1763. X        if (partn > 'z') partn = 'a';
  1764. X        if (part[0] != partn) {
  1765. X            msg("PARTS NOT IN SEQUENCE: %s -- removed", arcname);
  1766. X            user_delay(5);
  1767. X            fclose(out);
  1768. X            unlink(ofname);
  1769. X            new_file();
  1770. X            state = FIND_BEGIN_AFTER_ERROR;
  1771. X            goto err;
  1772. X        }
  1773. X        } else {
  1774. X        if(sscanf(line,"begin%o%s", &mode, arcname) != 2)
  1775. X            continue;
  1776. X
  1777. X        if (target != NULL)
  1778. X            sprintf(ofname, "%s%s", target, arcname);
  1779. X        else
  1780. X            strcpy(ofname, arcname);
  1781. X
  1782. X        if ((out = open_file(ofname, OPEN_CREATE)) == NULL) {
  1783. X            msg("Cannot create file: %s", ofname);
  1784. X            goto err;
  1785. X        }
  1786. X        chmod(ofname, mode);
  1787. X
  1788. X        if (decode_header_file)
  1789. X            store_header(ah, in, target, decode_header_file);
  1790. X        }
  1791. X
  1792. X        state = DECODE_TEXT;
  1793. X        continue;
  1794. X
  1795. X     case SKIP_LEADING:
  1796. X        if (len > prefix_lgt &&
  1797. X        (!prefix_lgt || strncmp(buf, prefix_str, prefix_lgt) == 0)) {
  1798. X        state = decode_line(buf + prefix_lgt, len - prefix_lgt, 1);
  1799. X        if (state == DECODE_TEXT) {
  1800. X            if (++lead_check == MIN_DECODE_LEADING) {
  1801. X            fseek(in, start_offset, 0);
  1802. X            continue;
  1803. X            }
  1804. X            state = SKIP_LEADING;
  1805. X            continue;
  1806. X        }
  1807. X        } else {
  1808. X        set_prefix = decode_skip_prefix;
  1809. X        if (strncmp_skip(buf, "begin", 5) == 0 ||
  1810. X            strncmp_skip(buf, "table", 5) == 0)
  1811. X            state = NEW_BEGIN;
  1812. X        }
  1813. X
  1814. X        lead_check = 0;
  1815. X        start_offset = ftell(in);
  1816. X        continue;
  1817. X
  1818. X     case DECODE_TEXT:
  1819. X        if (len <= prefix_lgt ||
  1820. X        (prefix_lgt > 0 && strncmp(buf, prefix_str, prefix_lgt))) {
  1821. X        state = SKIP_TRAILING;
  1822. X        continue;
  1823. X        }
  1824. X        if (onedone == 0) {
  1825. X        msg("Decoding%s: %s (part %d)",
  1826. X            prefix_lgt ? " & Unsharing" : "", arcname, ++arcpart);
  1827. X
  1828. X        onedone = 1;
  1829. X        }
  1830. X        state = decode_line(buf + prefix_lgt, len - prefix_lgt, 0);
  1831. X        continue;
  1832. X
  1833. X     case FOUND_END:
  1834. X        real_size = ftell(out);
  1835. X        fclose(out);
  1836. X
  1837. X        if (ftell(in) >= ah->lpos || fgets(buf, sizeof buf, in) == NULL) {
  1838. X        new_file();
  1839. X        break;
  1840. X        }
  1841. X
  1842. X        if ((!prefix_lgt || strncmp(buf, prefix_str, prefix_lgt) == 0) &&
  1843. X        sscanf(buf + prefix_lgt, "size%ld", &expect_size) == 1 &&
  1844. X        real_size != expect_size) {
  1845. X
  1846. X        msg("%s decoded with wrong size %ld (exp. %ld)",
  1847. X            arcname, real_size, expect_size);
  1848. X        user_delay(3);
  1849. X        } else {
  1850. X        msg("%s complete", arcname);
  1851. X        user_delay(1);
  1852. X        }
  1853. X
  1854. X        new_file();
  1855. X        state = NEW_BEGIN;
  1856. X        continue;
  1857. X
  1858. X     case FOUND_INCLUDE:
  1859. X        state = FIND_BEGIN_AFTER_INCLUDE;
  1860. X        return 0;
  1861. X
  1862. X     case SKIP_TRAILING:
  1863. X        state = SKIP_LEADING;
  1864. X        return 0;
  1865. X
  1866. X     case DECODE_ERROR:
  1867. X        state = SKIP_TRAILING;
  1868. X        continue;
  1869. X
  1870. X     case OTHER_ERROR:
  1871. X        fclose(out);
  1872. X        new_file();
  1873. X        state = FIND_BEGIN_AFTER_ERROR;
  1874. X        goto err;
  1875. X    }
  1876. X
  1877. X    break;    /* break in switch => break in loop */
  1878. X    }
  1879. X
  1880. X    if (onedone) {
  1881. X    if (state == DECODE_TEXT) state = SKIP_LEADING;
  1882. X    return 0;
  1883. X    }
  1884. X
  1885. X    if (state == FIND_BEGIN_AFTER_ERROR) return -1;
  1886. X    msg("No 'begin' line");
  1887. X
  1888. X err:
  1889. X    user_delay(2);
  1890. X    return -1;
  1891. X}
  1892. END_OF_FILE
  1893.   if test 10831 -ne `wc -c <'decode.c'`; then
  1894.     echo shar: \"'decode.c'\" unpacked with wrong size!
  1895.   fi
  1896.   # end of 'decode.c'
  1897. fi
  1898. if test -f 'digest.c' -a "${1}" != "-c" ; then 
  1899.   echo shar: Will not clobber existing file \"'digest.c'\"
  1900. else
  1901.   echo shar: Extracting \"'digest.c'\" \(7597 characters\)
  1902.   sed "s/^X//" >'digest.c' <<'END_OF_FILE'
  1903. X/*
  1904. X *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  1905. X *
  1906. X *    Digest article handling
  1907. X */
  1908. X
  1909. X#include "config.h"
  1910. X#include "news.h"
  1911. X#include "debug.h"
  1912. X
  1913. X#ifdef DG_TEST
  1914. X
  1915. X#define TEST(fmt, x, y) if (Debug & DG_TEST) printf(fmt, x, y)
  1916. X
  1917. X#else
  1918. X
  1919. X#define TEST(fmt, x, y)
  1920. X
  1921. X#endif
  1922. X
  1923. X#define UNIFY 040
  1924. X
  1925. Xstatic char digest_pattern[] = "igest";
  1926. X
  1927. Xinit_digest_parsing()
  1928. X{
  1929. X    register char *m;
  1930. X
  1931. X    for (m = digest_pattern; *m; m++) *m |= UNIFY;
  1932. X}
  1933. X
  1934. X
  1935. Xis_digest()
  1936. X{
  1937. X    register char *subject;
  1938. X    register char c, *q, *m;
  1939. X
  1940. X    if ((subject = news.ng_subj) == NULL) return 0;
  1941. X
  1942. X    while (c = *subject++) {
  1943. X    if ((c | UNIFY) != ('d' | UNIFY)) continue;
  1944. X
  1945. X    q = subject; m = digest_pattern;
  1946. X    while ((c = *m++) && (*q++ | UNIFY) == c);
  1947. X    if (c == NUL) return 1;
  1948. X    }
  1949. X    return 0;
  1950. X}
  1951. X
  1952. X
  1953. X/*
  1954. X * expect that f is positioned at header of an article
  1955. X */
  1956. X
  1957. Xstatic int is_mmdf_folder = 0;
  1958. X
  1959. Xget_digest_article(f, hdrbuf)
  1960. XFILE *f;
  1961. Xnews_header_buffer hdrbuf;
  1962. X{
  1963. X    int cont;
  1964. X
  1965. X    digest.dg_hpos = ftell(f);
  1966. X    TEST("GET DIGEST hp=%ld\n", digest.dg_hpos, 0);
  1967. X
  1968. X    do {
  1969. X    if (!parse_digest_header(f, 0, hdrbuf)) return -1;
  1970. X    digest.dg_fpos = ftell(f);
  1971. X    TEST("END HEADER hp=%ld fp=%ld\n", digest.dg_hpos, digest.dg_fpos);
  1972. X    } while ((cont = skip_digest_body(f)) < 0);
  1973. X
  1974. X    TEST("END BODY lp=%ld next=%ld\n", digest.dg_lpos, ftell(f));
  1975. X
  1976. X    return cont;
  1977. X}
  1978. X
  1979. X#define BACKUP_LINES     50    /* remember class + offset for parsed lines */
  1980. X
  1981. X#define    LN_BLANK    0x01    /* blank line */
  1982. X#define    LN_DASHED    0x02    /* dash line */
  1983. X#define    LN_HEADER    0x04    /* (possible) header line */
  1984. X#define    LN_ASTERISK    0x08    /* asterisk line (near end) */
  1985. X#define    LN_END_OF    0x10    /* End of ... line */
  1986. X#define    LN_TEXT        0x20    /* unclassified line */
  1987. X
  1988. X
  1989. X/*
  1990. X * skip until 'Subject: ' (or End of digest) line is found
  1991. X * then backup till start of header
  1992. X */
  1993. X
  1994. X/*
  1995. X * Tuning parameters:
  1996. X *
  1997. X *    MIN_HEADER_LINES:    number of known header lines that must
  1998. X *                be found in a block to identify a new
  1999. X *                header
  2000. X *
  2001. X *    MAX_BLANKS_DASH        max no of blanks on a 'dash line'
  2002. X *
  2003. X *    MIN_DASHES        min no of dashes on a 'dash line'
  2004. X *
  2005. X *    MAX_BLANKS_ASTERISKS    max no of blanks on an 'asterisk line'
  2006. X *
  2007. X *    MIN_ASTERISKS        min no of asterisks on an 'asterisk line'
  2008. X *
  2009. X *    MAX_BLANKS_END_OF    max no of blanks before "End of "
  2010. X */
  2011. X
  2012. X#define    MIN_HEADER_LINES    2
  2013. X#define    MAX_BLANKS_DASH        3
  2014. X#define    MIN_DASHES        16
  2015. X#define    MAX_BLANKS_ASTERISK    1
  2016. X#define    MIN_ASTERISKS        10
  2017. X#define    MAX_BLANKS_END_OF    1
  2018. X
  2019. Xskip_digest_body(f)
  2020. Xregister FILE *f;
  2021. X{
  2022. X    off_t  backup_p[BACKUP_LINES];
  2023. X    int       line_type[BACKUP_LINES];
  2024. X    register int backup_index, backup_count;
  2025. X    int    more_header_lines, end_or_asterisks, blanks;
  2026. X    char   line[1024];
  2027. X    register char *cp;
  2028. X    char **dg_hdr_field();
  2029. X
  2030. X#define    decrease_index()    \
  2031. X    if (--backup_index < 0) backup_index = BACKUP_LINES - 1
  2032. X
  2033. X    backup_index = -1;
  2034. X    backup_count = 0;
  2035. X    end_or_asterisks = 0;
  2036. X
  2037. X    digest.dg_lines = 0;
  2038. X
  2039. X
  2040. X next_line:
  2041. X    more_header_lines = 0;
  2042. X
  2043. X next_possible_header_line:
  2044. X    digest.dg_lines++;
  2045. X
  2046. X    if (++backup_index == BACKUP_LINES) backup_index = 0;
  2047. X    if (backup_count < BACKUP_LINES) backup_count++;
  2048. X
  2049. X    backup_p[backup_index] = ftell(f);
  2050. X    line_type[backup_index] = LN_TEXT;
  2051. X
  2052. X    if (fgets(line, 1024, f) == NULL) {
  2053. X    TEST("end_of_file, bc=%d, lines=%d\n", backup_count, digest.dg_lines);
  2054. X
  2055. X    if (is_mmdf_folder) {
  2056. X        digest.dg_lpos = backup_p[backup_index];
  2057. X        is_mmdf_folder = 0;
  2058. X        return 0;
  2059. X    }
  2060. X
  2061. X    /* end of file => look for "****" or "End of" line */
  2062. X
  2063. X    if (end_or_asterisks)
  2064. X        while (--backup_count >= 0) {
  2065. X        --digest.dg_lines;
  2066. X        decrease_index();
  2067. X        if (line_type[backup_index] & (LN_ASTERISK | LN_END_OF)) break;
  2068. X        }
  2069. X
  2070. X    if (digest.dg_lines == 0) return 0;
  2071. X
  2072. X    while (--backup_count >= 0) {
  2073. X        --digest.dg_lines;
  2074. X        digest.dg_lpos = backup_p[backup_index];
  2075. X        decrease_index();
  2076. X        if ((line_type[backup_index] &
  2077. X        (LN_ASTERISK | LN_END_OF | LN_BLANK | LN_DASHED)) == 0)
  2078. X        break;
  2079. X    }
  2080. X
  2081. X    return 0;    /* no article follows */
  2082. X    }
  2083. X
  2084. X    TEST("\n>>%-.50s ==>>", line, 0);
  2085. X
  2086. X    if (line[0] == '\001' && strcmp(line, "\001\001\001\001\n") == 0) {
  2087. X    digest.dg_lpos = backup_p[backup_index];
  2088. X    if (!is_mmdf_folder) fseek(f, digest.dg_lpos, 0);
  2089. X    --digest.dg_lines;
  2090. X    is_mmdf_folder = 0;
  2091. X    return (digest.dg_lines <= 0) ? -1 : 1;
  2092. X    }
  2093. X
  2094. X    if (is_mmdf_folder) goto next_line;
  2095. X
  2096. X    for (cp = line; *cp && isascii(*cp) && isspace(*cp); cp++);
  2097. X
  2098. X    if (*cp == NUL) {
  2099. X    TEST("BLANK", 0, 0);
  2100. X    line_type[backup_index] = LN_BLANK;
  2101. X    goto next_line;
  2102. X    }
  2103. X
  2104. X    blanks = cp - line;
  2105. X
  2106. X    if (*cp == '-') {
  2107. X    if (blanks > MAX_BLANKS_DASH) goto next_line;
  2108. X
  2109. X    while (*cp == '-') cp++;
  2110. X    if (cp - line - blanks > MIN_DASHES) {
  2111. X        while (*cp && (*cp == '-' || (isascii(*cp) && isspace(*cp)))) cp++;
  2112. X        if (*cp == NUL) {
  2113. X        TEST("DASHED", 0, 0);
  2114. X
  2115. X        line_type[backup_index] = LN_DASHED;
  2116. X        }
  2117. X
  2118. X    }
  2119. X    goto next_line;
  2120. X    }
  2121. X
  2122. X    if (*cp == '*') {
  2123. X    if (blanks > MAX_BLANKS_ASTERISK) goto next_line;
  2124. X
  2125. X    while (*cp == '*') cp++;
  2126. X    if (cp - line - blanks > MIN_ASTERISKS) {
  2127. X        while (*cp && (*cp == '*' || (isascii(*cp) && isspace(*cp)))) cp++;
  2128. X        if (*cp == NUL) {
  2129. X        TEST("ASTERISK", 0, 0);
  2130. X        line_type[backup_index] = LN_ASTERISK;
  2131. X        end_or_asterisks++;
  2132. X        }
  2133. X    }
  2134. X    goto next_line;
  2135. X    }
  2136. X
  2137. X    if (blanks <= MAX_BLANKS_END_OF &&
  2138. X    *cp == 'E' && strncmp(cp, "End of ", 7) == 0) {
  2139. X    TEST("END_OF_", 0, 0);
  2140. X    line_type[backup_index] = LN_END_OF;
  2141. X    end_or_asterisks++;
  2142. X    goto next_line;
  2143. X    }
  2144. X
  2145. X    if (blanks == 0) {
  2146. X    if (dg_hdr_field(line, 0)) {
  2147. X        TEST("HEADER", 0, 0);
  2148. X
  2149. X        line_type[backup_index] = LN_HEADER;
  2150. X        if (++more_header_lines < MIN_HEADER_LINES)
  2151. X        goto next_possible_header_line;
  2152. X
  2153. X        /* found block with MIN_HEADER_LINES */
  2154. X
  2155. X        /* search for beginning of header */
  2156. X
  2157. X        TEST("\nSearch for start of header\n", 0, 0);
  2158. X
  2159. X        for (;;) {
  2160. X        fseek(f, backup_p[backup_index], 0);
  2161. X        --digest.dg_lines;
  2162. X        if (--backup_count == 0) break;
  2163. X        decrease_index();
  2164. X        if ((line_type[backup_index] & (LN_HEADER | LN_TEXT)) == 0)
  2165. X            break;
  2166. X        }
  2167. X
  2168. X        if (digest.dg_lines == 0) {
  2169. X        TEST("Skipped empty article\n", 0, 0);
  2170. X        return -1;
  2171. X        }
  2172. X
  2173. X        for (;;) {
  2174. X        digest.dg_lpos = backup_p[backup_index];
  2175. X        if (--backup_count < 0) break;
  2176. X        decrease_index();
  2177. X        if ((line_type[backup_index] & (LN_BLANK | LN_DASHED)) == 0)
  2178. X            break;
  2179. X        --digest.dg_lines;
  2180. X        }
  2181. X
  2182. X        return (digest.dg_lines == 0) ? -1 : 1;
  2183. X    }
  2184. X    goto next_possible_header_line;
  2185. X    }
  2186. X
  2187. X    goto next_line;
  2188. X}
  2189. X
  2190. X
  2191. Xparse_digest_header(f, all, hdrbuf)
  2192. XFILE *f;
  2193. Xint all;
  2194. Xnews_header_buffer hdrbuf;
  2195. X{
  2196. X    extern char *parse_header(), **dg_hdr_field();
  2197. X
  2198. X    digest.dg_date = digest.dg_from = digest.dg_subj = digest.dg_to = NULL;
  2199. X
  2200. X    parse_header(f, dg_hdr_field, all, hdrbuf);
  2201. X
  2202. X    return digest.dg_from || digest.dg_subj;
  2203. X}
  2204. X
  2205. X
  2206. Xstatic char **dg_hdr_field(lp, all)
  2207. Xregister char *lp;
  2208. Xint all;
  2209. X{
  2210. X
  2211. X#define check(name, lgt, field) \
  2212. X    if (isascii(lp[lgt]) && isspace(lp[lgt]) && strncmp(name, lp, lgt) == 0) {\
  2213. X    TEST("MATCH: field ", 0, 0); \
  2214. X    return &digest.field; \
  2215. X    }
  2216. X
  2217. X
  2218. X    TEST("\nPARSE[%.20s] ==>> ", lp, 0);
  2219. X
  2220. X    switch (*lp++) {
  2221. X
  2222. X     case '\001':
  2223. X    if (!is_mmdf_folder && strncmp(lp, "\001\001\001\n", 4) == 0) {
  2224. X        is_mmdf_folder = 1;
  2225. X        digest.dg_hpos += 5;
  2226. X        return NULL;
  2227. X    }
  2228. X    break;
  2229. X
  2230. X     case 'D':
  2231. X     case 'd':
  2232. X    check("ate:",    4, dg_date);
  2233. X    break;
  2234. X
  2235. X     case 'F':
  2236. X     case 'f':
  2237. X    check("rom:",    4, dg_from);
  2238. X    break;
  2239. X
  2240. X     case 'R':
  2241. X     case 'r':
  2242. X    if (!all) break;
  2243. X    check("e:",    2, dg_subj);
  2244. X    break;
  2245. X
  2246. X     case 'S':
  2247. X     case 's':
  2248. X    check("ubject:", 7, dg_subj);
  2249. X    check("ubject",    6, dg_subj);
  2250. X    break;
  2251. X
  2252. X     case 'T':
  2253. X     case 't':
  2254. X    check("itle:",    5, dg_subj);
  2255. X    if (!all) break;
  2256. X    check("o:",    2, dg_to);
  2257. X    break;
  2258. X    }
  2259. X
  2260. X#undef check
  2261. X    TEST("NOT MATCHED ", 0, 0);
  2262. X
  2263. X    return NULL;
  2264. X}
  2265. END_OF_FILE
  2266.   if test 7597 -ne `wc -c <'digest.c'`; then
  2267.     echo shar: \"'digest.c'\" unpacked with wrong size!
  2268.   fi
  2269.   # end of 'digest.c'
  2270. fi
  2271. echo shar: End of archive 17 \(of 22\).
  2272. cp /dev/null ark17isdone
  2273. MISSING=""
  2274. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do
  2275.     if test ! -f ark${I}isdone ; then
  2276.     MISSING="${MISSING} ${I}"
  2277.     fi
  2278. done
  2279. if test "${MISSING}" = "" ; then
  2280.     echo You have unpacked all 22 archives.
  2281.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2282. else
  2283.     echo You still must unpack the following archives:
  2284.     echo "        " ${MISSING}
  2285. fi
  2286. exit 0
  2287.  
  2288. exit 0 # Just in case...
  2289.